using System;
using System.CodeDom.Compiler;
using System.IO;
using System.Web.Razor;
using System.Web.Razor.Generator;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.CSharp;
using Microsoft.VisualBasic;

namespace RazorGenerator
{
	/// <summary>
	/// Razor生成器任务
	/// </summary>
	public class RazorGeneratorTask : Task
	{
		/// <summary>
		/// 项目路径
		/// </summary>
		[Required]
		public string ProjectPath { get; set; }

		/// <summary>
		/// 生成路径
		/// </summary>
		[Required]
		public string OutputPath { get; set; }

		/// <summary>
		/// 编程语言
		/// </summary>
		public string Language { get; set; }

		/// <summary>
		/// 根命名空间
		/// </summary>
		public string RootNamespace { get; set; }

		public override bool Execute()
		{
			RazorCodeLanguage razorCodeLanguage;
			CodeDomProvider codeDomProvider;
			if ("vb".Equals(Language, StringComparison.CurrentCultureIgnoreCase))
			{
				Language = "vb";
				razorCodeLanguage = new VBRazorCodeLanguage();
				codeDomProvider = new VBCodeProvider();
			}
			else
			{
				Language = "cs";
				razorCodeLanguage = new CSharpRazorCodeLanguage();
				codeDomProvider = new CSharpCodeProvider();
			}

			Log.LogMessage(MessageImportance.High, $"Razor {ProjectPath} --> {OutputPath}");
			Log.LogMessage(MessageImportance.High, $"Razor Language: {Language}");
			Log.LogMessage(MessageImportance.High, $"Razor RootNamespace: {RootNamespace}");
			var razorEngineHost = new RazorEngineHost(razorCodeLanguage)
			{
				TabSize = 4,
				IsIndentingWithTabs = true,
				GeneratedClassContext = new GeneratedClassContext(
					executeMethodName: GeneratedClassContext.DefaultExecuteMethodName,
					writeMethodName: GeneratedClassContext.DefaultWriteMethodName,
					writeLiteralMethodName: GeneratedClassContext.DefaultWriteLiteralMethodName,
					writeToMethodName: "WriteTo",
					writeLiteralToMethodName: "WriteLiteralTo",
					templateTypeName: typeof(HelperTemplate).FullName,
					defineSectionMethodName: "DefineSection"
				)
			};
			var engine = new RazorTemplateEngine(razorEngineHost);
			ProjectPath = Path.GetFullPath(ProjectPath);
			OutputPath = Path.GetFullPath(OutputPath);
			foreach (string file in Directory.GetFiles(ProjectPath, "*.razor", SearchOption.AllDirectories))
			{
				string className = Path.GetFileNameWithoutExtension(file);
				string relativePath = Path.GetFullPath(Path.GetDirectoryName(file)!).Replace(ProjectPath, string.Empty).Trim(Path.DirectorySeparatorChar);
				string ns = (RootNamespace ?? string.Empty).TrimEnd('.');
				if (!string.IsNullOrWhiteSpace(relativePath))
				{
					ns += "." + relativePath.Replace(Path.DirectorySeparatorChar, '.');
				}

				string src = Path.GetFullPath(file);
				string dest = Path.Combine(OutputPath, relativePath, className + "." + Language);
				Log.LogMessage(MessageImportance.High, $"Razor {src} --> {dest}");
				Directory.CreateDirectory(Path.GetDirectoryName(dest)!);
				using var streamReader = new StreamReader(File.OpenRead(src));
				var generatorResults = engine.GenerateCode(streamReader, className, ns, src);
				if (!generatorResults.Success)
				{
					foreach (var razorError in generatorResults.ParserErrors)
					{
						Log.LogError(
							subcategory: string.Empty,
							errorCode: "Razor",
							helpKeyword: string.Empty,
							file: src,
							lineNumber: razorError.Location.LineIndex + 1, columnNumber: razorError.Location.CharacterIndex,
							endLineNumber: razorError.Location.LineIndex + 1, endColumnNumber: razorError.Location.CharacterIndex,
							message: razorError.Message
						);
					}
				}

				File.Delete(dest);
				using var writer = new StreamWriter(File.OpenWrite(dest));
				codeDomProvider.GenerateCodeFromCompileUnit(generatorResults.GeneratedCode, writer, new CodeGeneratorOptions());
			}

			return true;
		}
	}
}
